Overview

Exception mappings are a powerful feature for dealing with an Action that throws an exception. The core idea is that exception throwns during the Action method can be caught and mapped to a result, either global or action scoped results. This is especially useful for frameworks, like Hibernate and Acegisecurity, that throw RuntimeExceptions.

As with many other parts of WebWork, an interceptor is needed to activate the exception mapping functionality. Below is a snippet from webwork-default.xml which has exception mapping already activated.

snippet of webwork-default.xml
...
<interceptors>
    ...
    <interceptor name="exception" class="com.opensymphony.xwork.interceptor.ExceptionMappingInterceptor"/>
    ...
</interceptors>

<!-- Basic stack -->
<interceptor-stack name="basicStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="servlet-config"/>
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="static-params"/>
    <interceptor-ref name="params"/>
    <interceptor-ref name="conversionError"/>
</interceptor-stack>
...

 

The next step in exception mapping is to actually map exception to specific results. WebWork provides two ways to declare an exception mapping <exception-mapping/> - globally or for a specific action. The exception mapping element takes two attributes, exception and result.

When declaring an exception mapping, the interceptor will find the closest class inheritance match between the exception thrown and the exception declared. The interceptor will examine all declared mappings applicable to the Action, both Action specific and global mappings. The result (either global or Action scope) is then used.

This follows the same rules as a result returned from an Action. It first looks for the result in the action, and then if not found, it looks for the result as a global result.

Below is an example of global and Action scoped exception mappings.

snippet from xwork.xml
<xwork>
    <package name="default">
        ...
        <global-results>
            <result name="login" type="redirect">/login.action</result>
            <result name="rootException" type="freemarker">/WEB-INF/views/exception.ftl</result>
        </global-results>

        <global-exception-mappings>
            <exception-mapping exception="java.sql.SQLException" result="sqlException"/>
            <exception-mapping exception="java.lang.Exception" result="rootException"/>
        </global-exception-mappings>
        ...
        <action name="myAction" class="...">
            <interceptor-ref name="exception" />
            <exception-mapping exception="com.acme.foo.SecurityException" result="login"/>
            <result name="sqlException" type="chain">sqlExceptionAction</result>
            <result name="success" type="freemarker">/WEB-INF/views/acme/success.ftl</result>
        </action>
        ...
    </package>
</xwork>

 

In the example above, here is what happens based upon each exception:

  • A java.sql.SQLException will chain to the sqlExceptionAction
  • A com.acme.foo.SecurityException will redirect to /login.action
  • Any other exception that extends java.lang.Exception will execute the FreeMarker result rootException for the page /WEB-INF/views/exception.ftl
Exception Values on the ValueStack

By default, the ExceptionMappingInterceptor adds the following values to the Value Stack:

  • exception - The exception object itself
  • exceptionStack - the value from the stack trace